// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include "syscall2.h"
#include <stdio.h>
#include <iostream>
#include "synch.h"
#include <stdlib.h>
#include "Process.h"

extern "C" { int bzero(char *, int); };

#ifdef NETWORK
#include "network.h"
#include "post.h"
#endif

using namespace std;

#ifdef NETWORK
int getRandomServer(){
	char* regbuf = new char[MaxMailSize];
	if (!regbuf)
	{	
		printf("%s","Registration buffer couldn't be created for some reason\n");
		return -1;
	}
	
	char scName = BIND_Rand;
	memcpy(regbuf, &scName, 1);
	
	//Create and send mail
	PacketHeader outPktHdr;
	MailHeader outMailHdr;
	
	outPktHdr.to = 0;  
	outMailHdr.to = 0;
	outMailHdr.from = currentThread->getMailbox();
	outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, regbuf); 
	if ( !success ) {
	  printf("The postOffice Send for BIND_Rand failed. The Binder is not running!\n");
		return -1;
	}
	
	PacketHeader inPktHdr;
	MailHeader inMailHdr;
	char *buf = new char[MaxMailSize];
	
	postOffice->Receive(currentThread->getMailbox(), &inPktHdr, &inMailHdr, buf);
	
	memcpy(&scName, buf, 1);
	while(scName == RPC_PING)
	{
		postOffice->Receive(currentThread->getMailbox(), &inPktHdr, &inMailHdr, buf);
		memcpy(&scName, buf, 1);
	}
	if(scName != BIND_Rand)
	{
		printf("ERROR: Received unknown reply '%i' to BIND_Rand request!\n", (int)scName);
		return -1;
	}
	
	int serverID = 0;
	memcpy(&serverID, buf+1, 8);
	
	return serverID;
}
#endif

int copyin(unsigned int vaddr, int len, char *buf) 
{
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) 
    {
		result = machine->ReadMem( vaddr, 1, paddr );
		while(!result) // FALL 09 CHANGES
		{
			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
		}	
		  
		 buf[n++] = *paddr;
		 
		 if ( !result ) 
		 {
		//translation failed
			return -1;
		 }

      vaddr++;
    }

    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) return;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Create\n");
	delete buf;
	return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("%s","Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput)
	{
		return;
	}
    
    if ( !(buf = new char[len]) ) {
	printf("%s","Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("%s","Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("%s","Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput)
	{
		return -1;
	}
    
    if ( !(buf = new char[len]) ) 
	{
		printf("%s","Error allocating kernel buffer in Read\n");
		return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf("%s", buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("%s","Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("%s","Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("%s","Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("%s","Tried to close an unopen file\n");
    }
}

int Create_Lock_Syscal(unsigned int vaddr, int len)
{
#ifndef NETWORK
	//local code
	//checks that the length of the name is a valid length
	if(len<0)
	{
		printf("%s","Length cannot be less than 0\n");
		return -1;
	}
	char *buf = new char[len+1];	// Kernel buffer to put the name in
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return-1;
	}

	if( copyin(vaddr,len,buf) == -1 ) 
	{
		printf("%s","Bad pointer passed to Create\n");
		delete buf;
		return -1;
	}
	kernelLocksLock->Acquire();
	//Check that there are still locks avaible to be created
	if(nextAvailableLock>=NUM_KERNEL_LOCKS)
	{
		printf("%s","Cannot create anymore locks\n");
		kernelLocksLock->Release();
		delete buf;
		return -1;
	}
	int lockNumber=nextAvailableLock;
	kernelLocks[lockNumber]=new KernelLock(buf);
	nextAvailableLock++;
	kernelLocksLock->Release();
	delete buf;
	return lockNumber;
#endif
#ifdef NETWORK
	//RPC code
	
	//ensure len > 0
	if(len<0)
	{
		printf("%s","Length cannot be less than 0\n");
		return -1;
	}
	
	//ensure name is not too short
	if(len>(MaxMailSize-9))
	{
		printf("%s%d%s","Length cannot be more than ",MaxMailSize-9,"\n");
		return -1;
	}
	
	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return-1;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_CreateLock;
	//printf("\nscName 1: %c\n", *scName);
	memcpy(buf,scName,1); 
	//printf("scName 2: %c\n", *buf);
	//copy in len
	memcpy(buf+1,&len,8);
	
	//copy in name
	if( copyin(vaddr,len,buf+9) == -1 ) 
	{
		printf("%s","Failed to copy name into create lock message\n");
		delete buf;
		return -1;
	}
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for Create Lock failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	
	 // Wait for the server reply
   
	do
	{
		postOffice->Receive(0, &inPktHdr, &inMailHdr, buf);
		memcpy(scName, buf, 1);
		printf("recieved message\n");
	
	}while(*scName==RPC_PING);
	
	if(*scName == RPC_LockCreated)
	{
		printf("Received a reply from server for Create Lock\n");
	}else{
		printf("ERROR: Received unknown reply '%d' from server for Create Lock\n", *scName);
		return -1;
	}
	
	//server reply should just be an int - like Crowley said, can't really validate response
	int* lockNumber = new int;
	memcpy(lockNumber, buf+1, 8);
	
	return *lockNumber;
#endif
}

void Acquire_Lock_Syscall(int lockNum)
{
#ifndef NETWORK
	kernelLocksLock->Acquire();
	if(lockNum<0||lockNum>=nextAvailableLock)
	{
		printf("The lock requested, number %i, does not exist\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	if(!kernelLocks[lockNum]->available)
	{
		printf("The lock requested, number  %i, has been deleted\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	if(kernelLocks[lockNum]->toBeDeleted)
	{
		printf("The lock requested, number  %i, is scheduled to be deleted\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	if(currentThread->space!=kernelLocks[lockNum]->space)
	{
		printf("The lock requested, number  %i, belongs to a different address space\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	kernelLocksLock->Release();
	kernelLocks[lockNum]->lock->Acquire();
	return;
#endif

//netcode
#ifdef NETWORK

	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_AcquireLock;
	memcpy(buf,scName,1); 
	
	//copy in index
	memcpy(buf+1,&lockNum,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	printf("Acquiring lock:%i\n",lockNum);
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for Acquire Lock failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	
	do
	{
		postOffice->Receive(0, &inPktHdr, &inMailHdr, buf);
		memcpy(scName, buf, 1);
	
	}while(*scName==RPC_PING);
	
	if(*scName == RPC_LockAcquired)
	{
		printf("Received a reply from server for Acquire Lock\n");
	}else{
		printf("ERROR: Received unknown reply from server for Aqcuire Lock\n");
	}
	return;
#endif
}

void Release_Lock_Syscall(int lockNum)
{
#ifndef NETWORK
	kernelLocksLock->Acquire();
	if(lockNum<0||lockNum>=nextAvailableLock)
	{
		printf("The lock requested, number %i, does not exist\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	if(!kernelLocks[lockNum]->available)
	{
		printf("The lock requested, number  %i, has been deleted\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	if(currentThread->space!=kernelLocks[lockNum]->space)
	{
		printf("The lock requested, number  %i, belongs to a different address space\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	kernelLocks[lockNum]->lock->Release();
	if(kernelLocks[lockNum]->toBeDeleted&&kernelLocks[lockNum]->lock->numWaiting==0)
	{
		kernelLocks[lockNum]->toBeDeleted=false;
		kernelLocks[lockNum]->available=false;
		printf("Lock Deleted\n");
	}
	kernelLocksLock->Release();
	return;	
#endif

//netcode
#ifdef NETWORK

	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_ReleaseLock;
	memcpy(buf,scName,1); 
	
	//copy in index
	memcpy(buf+1,&lockNum,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for Release Lock failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

#endif
}

void Destroy_Lock_Syscall(int lockNum)
{
#ifndef NETWORK
	kernelLocksLock->Acquire();
	if(lockNum<0||lockNum>=nextAvailableLock)
	{
		printf("The lock requested, number %i, does not exist\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	if(!kernelLocks[lockNum]->available)
	{
		printf("The lock requested, number  %i, has already been deleted\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	if(kernelLocks[lockNum]->toBeDeleted)
	{
		printf("The lock requested, number  %i, is already scheduled to be deleted\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	if(currentThread->space!=kernelLocks[lockNum]->space)
	{
		printf("The lock requested, number  %i, belongs to a different address space\n",lockNum);
		kernelLocksLock->Release();
		return;
	}
	if(kernelLocks[lockNum]->lock->numWaiting==0)
	{
		kernelLocks[lockNum]->available=false;
	}
	else
	{
		kernelLocks[lockNum]->toBeDeleted=true;
		printf("Lock can't be deleted yet\n");
	}
	kernelLocksLock->Release();
	return;
	#endif

//netcode
#ifdef NETWORK

	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_DestroyLock;
	memcpy(buf,scName,1); 
	
	//copy in index
	memcpy(buf+1,&lockNum,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for Destroy Lock failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

#endif
}

int Create_Condition_Syscall(unsigned int vaddr, int len)
{
#ifndef NETWORK
	//checks that the length of the name is a valid length
	if(len<0)
	{
		printf("%s","Length cannot be less than 0\n");
		return -1;
	}
	char *buf = new char[len+1];	// Kernel buffer to put the name in
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return-1;
	}

	if( copyin(vaddr,len,buf) == -1 ) 
	{
		printf("%s","Bad pointer passed to Create\n");
		delete buf;
		return -1;
	}
	kernelCondsLock->Acquire();
	//Check that there are still locks avaible to be created
	if(nextAvailableCond>=NUM_KERNEL_CONDS)
	{
		printf("%s","Cannot create anymore conditions\n");
		delete buf;
		kernelCondsLock->Release();
		return -1;
	}
	int condNumber=nextAvailableCond;
	kernelConds[condNumber]=new KernelCondition(buf);
	nextAvailableCond++;
	kernelCondsLock->Release();
	delete buf;
	return condNumber;
#endif

#ifdef NETWORK
	//RPC code
	
	//ensure len > 0
	if(len<0)
	{
		printf("%s","Length cannot be less than 0\n");
		return -1;
	}
	
	//ensure name is not too short
	if(len>(MaxMailSize-9))
	{
		printf("%s%d%s","Length cannot be more than ",MaxMailSize-9,"\n");
		return -1;
	}
	
	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return-1;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_CreateCondition;
	memcpy(buf,scName,1); 
	
	//copy in len
	memcpy(buf+1,&len,8);
	
	//copy in name
	if( copyin(vaddr,len,buf+9) == -1 ) 
	{
		printf("%s","Failed to copy name into create condition message\n");
		delete buf;
		return -1;
	}
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for Create Condition failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	
	 // Wait for the server reply
	do
	{
		postOffice->Receive(0, &inPktHdr, &inMailHdr, buf);
		memcpy(scName, buf, 1);
	
	}while(*scName==RPC_PING);
	
	if(*scName == RPC_CondCreated)
	{
		printf("Received a reply from server for Create Condition\n");
	}else{
		printf("ERROR: Received unknown reply from server for Create Condition\n");
		return -1;
	}
	
	//server reply should just be an int - like Crowley said, can't really validate response
	int* condNumber = new int;
	memcpy(condNumber, buf+1, 8);
	
	return *condNumber;
#endif
}

void Wait_Syscall(int condNum,int lockNum)
{
#ifndef NETWORK
	kernelLocksLock->Acquire();
	kernelCondsLock->Acquire();
	if(lockNum<0||lockNum>=nextAvailableLock)
	{
		printf("The lock requested, number %i, does not exist\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(!kernelLocks[lockNum]->available)
	{
		printf("The lock requested, number  %i, has been deleted\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(kernelLocks[lockNum]->toBeDeleted)
	{
		printf("The lock requested, number  %i, is scheduled to be deleted\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(currentThread->space!=kernelLocks[lockNum]->space)
	{
		printf("The lock requested, number  %i, belongs to a different address space\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	
	if(condNum<0||condNum>=nextAvailableCond)
	{
		printf("The condition requested, number %i, does not exist\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(!kernelConds[condNum]->available)
	{
		printf("The condition requested, number  %i, has been deleted\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(kernelConds[condNum]->toBeDeleted)
	{
		printf("The condition requested, number  %i, is scheduled to be deleted\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(currentThread->space!=kernelConds[condNum]->space)
	{
		printf("The condition requested, number  %i, belongs to a different address space\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	
	//I don't need to make sure that the lock and condition belong to the same address space since I made sure both belong to the current one
	kernelLocksLock->Release();
	kernelCondsLock->Release();
	
	kernelConds[condNum]->cond->Wait(kernelLocks[lockNum]->lock);
	return;
#endif
	
//netcode
#ifdef NETWORK

	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_Wait;
	memcpy(buf,scName,1); 
	
	//copy in condNum
	memcpy(buf+1,&condNum,8);
	
	//copy in lockNum
	memcpy(buf+9,&lockNum,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for Wait failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	
	do
	{
		postOffice->Receive(0, &inPktHdr, &inMailHdr, buf);
		memcpy(scName, buf, 1);
	
	}while(*scName==RPC_PING);
	
	if(*scName == RPC_LockAcquired)
	{
		printf("Received a reply from server for Wait\n");
	}else{
		printf("ERROR: Received unknown reply '%d' from server for Wait\n", *scName);
	}

#endif
}

void Broadcast_Syscall(int condNum,int lockNum)
{
#ifndef NETWORK
	kernelLocksLock->Acquire();
	kernelCondsLock->Acquire();
	if(lockNum<0||lockNum>=nextAvailableLock)
	{
		printf("The lock requested, number %i, does not exist\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(!kernelLocks[lockNum]->available)
	{
		printf("The lock requested, number  %i, has been deleted\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(kernelLocks[lockNum]->toBeDeleted)
	{
		printf("The lock requested, number  %i, is scheduled to be deleted\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(currentThread->space!=kernelLocks[lockNum]->space)
	{
		printf("The lock requested, number  %i, belongs to a different address space\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	
	if(condNum<0||condNum>=nextAvailableCond)
	{
		printf("The condition requested, number %i, does not exist\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(!kernelConds[condNum]->available)
	{
		printf("The condition requested, number  %i, has been deleted\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(kernelConds[condNum]->toBeDeleted)
	{
		printf("The condition requested, number  %i, is scheduled to be deleted\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(currentThread->space!=kernelConds[condNum]->space)
	{
		printf("The condition requested, number  %i, belongs to a different address space\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	
	//I don't need to make sure that the lock and condition belong to the same address space since I made sure both belong to the current one
	
	kernelConds[condNum]->cond->Broadcast(kernelLocks[lockNum]->lock);
	
	if(kernelLocks[lockNum]->toBeDeleted)
	{
		kernelLocks[lockNum]->toBeDeleted=false;
		kernelLocks[lockNum]->available=false;
		printf("Lock Deleted\n");
	}
	
	if(kernelConds[condNum]->toBeDeleted)
	{
		kernelConds[condNum]->toBeDeleted=false;
		kernelConds[condNum]->available=false;
		printf("Condition Deleted\n");
	}
	kernelLocksLock->Release();
	kernelCondsLock->Release();
	return;
#endif

#ifdef NETWORK

	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_Broadcast;
	memcpy(buf,scName,1); 
	
	//copy in condNum
	memcpy(buf+1,&condNum,8);
	
	//copy in lockNum
	memcpy(buf+9,&lockNum,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for Broadcast failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
#endif
}

void Signal_Syscall(int condNum,int lockNum)
{
#ifndef NETWORK
	kernelLocksLock->Acquire();
	kernelCondsLock->Acquire();
	if(lockNum<0||lockNum>=nextAvailableLock)
	{
		printf("The lock requested, number %i, does not exist\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(!kernelLocks[lockNum]->available)
	{
		printf("The lock requested, number  %i, has been deleted\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(kernelLocks[lockNum]->toBeDeleted)
	{
		printf("The lock requested, number  %i, is scheduled to be deleted\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(currentThread->space!=kernelLocks[lockNum]->space)
	{
		printf("The lock requested, number  %i, belongs to a different address space\n",lockNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	
	if(condNum<0||condNum>=nextAvailableCond)
	{
		printf("The condition requested, number %i, does not exist\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(!kernelConds[condNum]->available)
	{
		printf("The condition requested, number  %i, has been deleted\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(kernelConds[condNum]->toBeDeleted)
	{
		printf("The condition requested, number  %i, is scheduled to be deleted\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	if(currentThread->space!=kernelConds[condNum]->space)
	{
		printf("The condition requested, number  %i, belongs to a different address space\n",condNum);
		kernelLocksLock->Release();
		kernelCondsLock->Release();
		return;
	}
	
	//I don't need to make sure that the lock and condition belong to the same address space since I made sure both belong to the current one
	
	kernelConds[condNum]->cond->Broadcast(kernelLocks[lockNum]->lock);
	
	if(kernelLocks[lockNum]->toBeDeleted&&kernelLocks[lockNum]->lock->numWaiting==0)
	{
		kernelLocks[lockNum]->toBeDeleted=false;
		kernelLocks[lockNum]->available=false;
		printf("Lock Deleted\n");
	}
	
	if(kernelConds[condNum]->toBeDeleted&&kernelConds[condNum]->cond->numWaiting==0)
	{
		kernelConds[condNum]->toBeDeleted=false;
		kernelConds[condNum]->available=false;
		printf("Condition Deleted\n");
	}
	kernelLocksLock->Release();
	kernelCondsLock->Release();
	return;
#endif

#ifdef NETWORK

	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_Signal;
	memcpy(buf,scName,1); 
	
	//copy in condNum
	memcpy(buf+1,&condNum,8);
	
	//copy in lockNum
	memcpy(buf+9,&lockNum,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for Signal failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
#endif
}

void Destroy_Condition_Syscall(int condNum)
{
#ifndef NETWORK
	kernelCondsLock->Acquire();
	if(condNum<0||condNum>=nextAvailableCond)
	{
		printf("The condition requested, number %i, does not exist\n",condNum);
		kernelCondsLock->Release();
		return;
	}
	if(kernelLocks[condNum]->available==false)
	{
		printf("The condition requested, number  %i, has already been deleted\n",condNum);
		kernelCondsLock->Release();
		return;
	}
	if(kernelLocks[condNum]->toBeDeleted)
	{
		printf("The condition requested, number  %i, is already scheduled to be deleted\n",condNum);
		kernelCondsLock->Release();
		return;
	}
	if(currentThread->space!=kernelLocks[condNum]->space)
	{
		printf("The condition requested, number  %i, belongs to a different address space\n",condNum);
		kernelCondsLock->Release();
		return;
	}
	if(kernelConds[condNum]->cond->numWaiting==0)
	{
		kernelConds[condNum]->available=false;
		printf("~~Cond deleted\n");
	}
	else
	{
		kernelConds[condNum]->toBeDeleted=true;
		printf("Condition can't be deleted yet\n");
	}
	kernelCondsLock->Release();
	return;
#endif

//netcode
#ifdef NETWORK

	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_DestroyCondition;
	memcpy(buf,scName,1); 
	
	//copy in index
	memcpy(buf+1,&condNum,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for Destroy Condition failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

#endif
}

int CreateMV(int rows, int cols, int len, unsigned int vaddr)
{
	#ifdef NETWORK
	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return -1;
	}
	if(len > 28 || len < 1)
	{
		printf("ERROR: MV name must be 1-28 chars in length!\n");
		return -1;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_CreateMV;
	memcpy(buf,scName,1); 
	
	//copy in rows
	char rChar = rows;
	memcpy(buf+1,&rChar,1);
	
	//copy in cols
	char cChar = cols;
	memcpy(buf+2,&cChar,1);
	
	//copy in len
	char lChar = len;
	memcpy(buf+3,&lChar,1);
	
	//copy in name
	//memcpy(buf+4,name,len);
	if( copyin(vaddr,len,buf+4) == -1 ) 
	{
		printf("%s","Bad pointer passed to CreateMV\n");
		delete buf;
		return -1;
	}
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for CreateMV failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	
	do
	{
		postOffice->Receive(0, &inPktHdr, &inMailHdr, buf);
		memcpy(scName, buf, 1);
	
	}while(*scName==RPC_PING);
	
	if(*scName == RPC_MVCreated)
	{
		//return the index of the MV
		printf("Received a reply from server for CreateMV\n");
		int* value = new int;
		memcpy(value, buf+1, 8);
		
		return *value;
		
	}else{
		printf("ERROR: Received unknown reply from server for CreateMV\n");
		return -1;
	}
	#endif
	return -1;
}

int GetMV(int index, int row, int col)
{
	#ifdef NETWORK
	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return -1;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_GetMV;
	memcpy(buf,scName,1); 
	
	//copy in index
	memcpy(buf+1,&index,8);
	
	//copy in row
	memcpy(buf+9,&row,8);
	
	//copy in col
	memcpy(buf+17,&col,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for GetMV failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	do
	{
		postOffice->Receive(0, &inPktHdr, &inMailHdr, buf);
		memcpy(scName, buf, 1);
	
	}while(*scName==RPC_PING);
	
	if(*scName == RPC_MVValue)
	{
		//return the value of  MV[index][r][c]
		printf("Received a reply from server for GetMV\n");
		int* value = new int;
		memcpy(value, buf+1, 8);
		
		return *value;
		
	}else{
		printf("ERROR: Received unknown reply from server for GetMV\n");
		return -1;
	}
	#endif
	return -1;
}

//because of message size restraints, have to convert r/c to chars before sending
void SetMV(int index, int row, int col, int val)
{
	#ifdef NETWORK
	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return;
	}
	
	//convert r,c to chars
	char r_char = row;
	char c_char = col;
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_SetMV;
	memcpy(buf,scName,1); 
	
	//copy in index
	memcpy(buf+1,&index,8);
	
	//copy in row
	memcpy(buf+9,&r_char,1);
	
	//copy in col
	memcpy(buf+10,&c_char,1);
	
	//copy in value
	memcpy(buf+11,&val,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for SetMV failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	#endif
}

void DestroyMV(int index)
{
	#ifdef NETWORK
	//create mail data buffer
	char *buf = new char[MaxMailSize];
	if (!buf)
	{	
		printf("%s","Buffer couldn't be created for some reason\n");
		return;
	}
	
	//copy in syscall id
	char* scName = new char;
	*scName = SC_DestroyMV;
	memcpy(buf,scName,1); 
	
	//copy in index
	memcpy(buf+1,&index,8);
	
	//Create and send mail
	PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
	
	outPktHdr.to = getRandomServer(); 		
    outMailHdr.to = 0;
    outMailHdr.from = currentThread->getMailbox();
    outMailHdr.length = MaxMailSize;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, buf); 

    if ( !success ) {
      printf("The postOffice Send for DestroyMV failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
	#endif
}

static void kernelThread(int vaddr)
{
	currentThread->space->InitRegisters();
	//Need to rewrite the values in those registers so, they go to the right place.
    // Initial program counter -- must be location of "Start"
    machine->WriteRegister(PCReg, vaddr);	

    // Need to also tell MIPS where next instruction is, because
    // of branch delay possibility
    machine->WriteRegister(NextPCReg,vaddr+4);
	currentThread->space->RestoreState();
	currentThread->SetMBID();
	machine->Run();
}


void Fork_Syscall(unsigned int vaddr)
{
	processLock->Acquire();
	char *buf= new char[20];
	sprintf(buf,"thread %i\0",processTable[currentThread->space->spaceId]->threads);
	Thread *t =new Thread(buf);
	t->space=currentThread->space;
	t->spaceId=currentThread->spaceId;
	
	int spaceId=t->space->spaceId;
	processTable[spaceId]->lock->Acquire();
	processTable[spaceId]->threads++;
	processTable[spaceId]->lock->Release();
	
	t->Fork(kernelThread,vaddr);
	processLock->Release();
	return;
}

void Exit_Syscall(int value)
{
	if(value!=0)
	{
		printf("Exited on %i\n",value);
	}
	int spaceId=currentThread->space->spaceId;
	processTable[spaceId]->lock->Acquire();
	processTable[spaceId]->threads--;
	if(processTable[spaceId]->threads>0)
	{
		processTable[spaceId]->lock->Release();
		currentThread->Finish();
		return;
	}
	else
	{
		processLock->Acquire();
		processTable[spaceId]->lock->Release();
		processTable.erase(spaceId);
		//remove from the IPT anything from this addrspace
		#ifdef FILESYS_NEEDED		
		for(int i=0;i<NumPhysPages;i++)
		{
			if(IPT[i].spaceId==spaceId)
			{
				IPT[i].valid=false;
				fifoManager.remove(i);
				physicalMemoryTable->Remove(i);
			}
		}
		#endif
		if(processTable.size()==0)
		{
			processLock->Release();
			interrupt->Halt();
			return;
		}
		else
		{
		processLock->Release();
		currentThread->Finish();
		return;
		}
	}
}

static void exec_Thread(int useless)
{
	currentThread->space->InitRegisters();
	currentThread->space->RestoreState();
	currentThread->SetMBID();
	machine->Run();
}

SpaceId Exec_Syscall(unsigned int vaddr, int len)
{
	char *buf = new char[len+1];	// Kernel buffer to put the name in
	
    if (!buf) return -1;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Exec\n");
	delete buf;
	return -1;
    }

    buf[len]='\0';
	
	 OpenFile *executable = fileSystem->Open(buf);
    AddrSpace *space;

    if (executable == NULL) {
	printf("Unable to open file %s\n", buf);
	return -1;
    }
   
    space = new AddrSpace(executable);

	Thread *t=new Thread(buf);
    t->space = space;
	t->spaceId=space->spaceId;

    //delete executable;			// close file
	t->Fork(exec_Thread,0);
	return space->spaceId;
}

int Random_Syscall()
{
	static int Init = 0;
	if(!Init)
	{
		srand ( time(NULL) );
		Init = 1;
	}
	return (int)( rand() % 100 );
}

int ReadInt_Syscall(unsigned int vaddr, int len)
{
	char *buf = new char[len+1];	// Kernel buffer to put the name in
	
    if (!buf) return -1;

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("%s","Bad pointer passed to Read int\n");
	delete buf;
	return -1;
    }

    buf[len]='\0';
	printf(buf);
	//Print out a message related to the read
	int* read;
	int error;
	error=scanf("%d",read);
	while(error==EOF)
	{
		printf("The number entered was not valid\n");
		printf(buf);
		error=scanf("%d",read);
	}
	return *read;
}

int Itoa_Syscall(int num, unsigned int vaddr)
{
	char buf[15];
	sprintf(buf,"%d",num);
	int i=0;
	while(buf[i]!='\0'&&i<15)
	{
		i++;
	}
	copyout(vaddr,i,buf);
	return i;
}

//Don't need to make this method thread secure internally since it is only called inside of something that is secured
int handleFullMemory()
{
	int physPage;
	if(USERAND)
	{
		static int Init = 0;
		if(!Init)
		{
		srand ( time(NULL) );
		}
		Init = 1;
		physPage=(int)( rand() % NumPhysPages );
	}
	else
	{
		physPage=fifoManager.front();
		fifoManager.pop_front();
	}
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	if(IPT[physPage].tlbLoc!=-1)
	{
		if(machine->tlb[IPT[physPage].tlbLoc].dirty)
		{
			IPT[physPage].dirty=true;
		}
		//See it is no longer in memory, the TLB entry is invalid
		machine->tlb[IPT[physPage].tlbLoc].valid=false;
	}
	(void) interrupt->SetLevel(oldLevel);
	AddrSpace* space=processTable[IPT[physPage].spaceId]->space;
	if(IPT[physPage].dirty)
	{
		space->pageTable[IPT[physPage].virtualPage].dirty=true;
	}
	if(space->pageTable[IPT[physPage].virtualPage].dirty)
	{
		//copy info out and set diskLocation=2
		
		int swapLoc=swapMap->Find();
		if(swapLoc==-1)
		{
			printf("Swap Map full.  Exiting \n");
			interrupt->Halt();
		}
		swapFile->WriteAt(&(machine->mainMemory[physPage*PageSize]),PageSize,swapLoc*PageSize);
		space->pageTable[IPT[physPage].virtualPage].diskLocation=2;
		space->pageTable[IPT[physPage].virtualPage].swapLocation=swapLoc;
	}
	return physPage;
}

int handleIPTMiss(int vpn)
{
	MemorySemaphore->P();
	int physPage= physicalMemoryTable->Put(0);
	if(physPage==-1)
	{
		physPage=handleFullMemory();
	}
	fifoManager.push_back(physPage);
	bzero(machine->mainMemory+(physPage*PageSize), PageSize);
	if(currentThread->space->pageTable[vpn].diskLocation==0)
	{
		currentThread->space->xecutble->ReadAt(&(machine->mainMemory[physPage*PageSize]),
			PageSize, currentThread->space->pageTable[vpn].byteOffset);
	}
	else if(currentThread->space->pageTable[vpn].diskLocation==2)
	{
		int swapLoc=currentThread->space->pageTable[vpn].swapLocation;
		swapFile->ReadAt(&(machine->mainMemory[physPage*PageSize]),PageSize,swapLoc*PageSize);
		swapMap->Clear(swapLoc);
	}
	currentThread->space->pageTable[vpn].valid=true;
	IPT[physPage].valid=currentThread->space->pageTable[vpn].valid;
	IPT[physPage].use=currentThread->space->pageTable[vpn].use;
	IPT[physPage].dirty=currentThread->space->pageTable[vpn].dirty;
	IPT[physPage].readOnly=currentThread->space->pageTable[vpn].readOnly; 
	IPT[physPage].virtualPage=currentThread->space->pageTable[vpn].virtualPage;
	IPT[physPage].spaceId=currentThread->spaceId;
	currentThread->space->pageTable[vpn].physicalPage=physPage;
	IPT[physPage].physicalPage=currentThread->space->pageTable[vpn].physicalPage;
	IPT[physPage].tlbLoc=-1;	
	MemorySemaphore->V();
	return physPage;
	return 0;
}

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall

    if ( which == SyscallException )
	{
		switch (type) {
			default:
			DEBUG('a', "Unknown syscall - shutting down.\n");
			case SC_Halt:
			DEBUG('a', "Shutdown, initiated by user program.\n");
			interrupt->Halt();
			break;
			case SC_Create:
			DEBUG('a', "Create syscall.\n");
			Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_Open:
			DEBUG('a', "Open syscall.\n");
			rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_Write:
			DEBUG('a', "Write syscall.\n");
			Write_Syscall(machine->ReadRegister(4),
					  machine->ReadRegister(5),
					  machine->ReadRegister(6));
			break;
			case SC_Read:
			DEBUG('a', "Read syscall.\n");
			rv = Read_Syscall(machine->ReadRegister(4),
					  machine->ReadRegister(5),
					  machine->ReadRegister(6));
			break;
			case SC_Close:
			DEBUG('a', "Close syscall.\n");
			Close_Syscall(machine->ReadRegister(4));
			break;
			case SC_Yield:
			DEBUG('a',"Yield syscall.\n");
			currentThread->Yield();
			break;
			case SC_CreateLock:
			DEBUG('a',"Create Lock syscall.\n");
			rv=Create_Lock_Syscal(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_AcquireLock:
			DEBUG('a',"Acquire Lock syscall.\n");
			Acquire_Lock_Syscall(machine->ReadRegister(4));
			break;
			case SC_ReleaseLock:
			DEBUG('a',"Release Lock syscall.\n");
			Release_Lock_Syscall(machine->ReadRegister(4));
			break;
			case SC_DestroyLock:
			DEBUG('a',"Destroy Lock syscall.\n");
			Destroy_Lock_Syscall(machine->ReadRegister(4));
			break;
			case SC_CreateCondition:
			DEBUG('a',"Create Condition syscall.\n");
			rv=Create_Condition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_Wait:
			DEBUG('a',"Wait syscall.\n");
			Wait_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_Signal:
			DEBUG('a',"Signal syscall.\n");
			Signal_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_Broadcast:
			DEBUG('a',"Broadcast syscall.\n");
			Broadcast_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_DestroyCondition:
			DEBUG('a',"Destroy Condition syscall.\n");
			Destroy_Condition_Syscall(machine->ReadRegister(4));
			break;
			case SC_Fork:
			DEBUG('a',"Fork Syscall.\n");
			Fork_Syscall(machine->ReadRegister(4));
			break;
			case SC_Exit:
			DEBUG('a',"Exit syscall.\n");
			Exit_Syscall(machine->ReadRegister(4));
			break;
			case SC_Random:
			DEBUG('a',"Random syscall.\n");
			rv=Random_Syscall();
			break;
			case SC_Exec:
			DEBUG('a',"Exec Syscall.\n");
			rv=Exec_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_ReadInt:
			DEBUG('a',"Read Int Syscall.\n");
			rv=ReadInt_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_Itoa:
			DEBUG('a',"Itoa Syscall.\n");
			rv=Itoa_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_ConsoleAcquire:
			DEBUG('a',"Console lock acquire syscall.\n");
			consoleLock->Acquire();
			break;
			case SC_ConsoleRelease:
			DEBUG('a',"Console lock release syscall.\n");
			consoleLock->Release();
			break;
			case SC_CreateMV:
			DEBUG('a',"Console CreateMV syscall.\n");
			rv=CreateMV(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6), machine->ReadRegister(7));
			break;
			case SC_GetMV:
			DEBUG('a',"Console GetMV syscall.\n");
			rv=GetMV(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
			break;
			case SC_SetMV:
			DEBUG('a',"Console SetMV syscall.\n");
			SetMV(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6), machine->ReadRegister(7));
			break;
			case SC_DestroyMV:
			DEBUG('a',"Console DestroyMV syscall.\n");
			DestroyMV(machine->ReadRegister(4));
			break;
		}

		// Put in the return value and increment the PC
		machine->WriteRegister(2,rv);
		machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
		machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
		machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
		return;
    }
	else if(which==PageFaultException)
	{
		#ifdef FILESYS_NEEDED 
		//increment currentTlb
		currentTlb=(currentTlb+1)%TLBSize;
		int vpn=machine->ReadRegister(39)/PageSize;
		//Find the Page in the IPT
		int physicalPage=-1;
		for(int i=0;i<NumPhysPages;i++)
		{
			if(IPT[i].valid  &&  IPT[i].virtualPage==vpn  &&  IPT[i].spaceId==currentThread->spaceId)
			{
				physicalPage=i;
				break;
			}
		}
		if(physicalPage==-1)
		{
			physicalPage=handleIPTMiss(vpn);
		}
		#endif
		IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
		//copy old info out of tlb
		if(machine->tlb[currentTlb].dirty&&IPT[machine->tlb[currentTlb].physicalPage].tlbLoc==currentTlb)
		{
			IPT[machine->tlb[currentTlb].physicalPage].dirty=true;
			IPT[machine->tlb[currentTlb].physicalPage].tlbLoc=-1;
		}
		//Copy everything into the tlb
		machine->tlb[currentTlb].virtualPage=IPT[physicalPage].virtualPage;
		machine->tlb[currentTlb].physicalPage=IPT[physicalPage].physicalPage;
		machine->tlb[currentTlb].valid=IPT[physicalPage].valid;
		machine->tlb[currentTlb].readOnly=IPT[physicalPage].readOnly;
		machine->tlb[currentTlb].use=IPT[physicalPage].use;
		machine->tlb[currentTlb].dirty=IPT[physicalPage].dirty;
		IPT[physicalPage].tlbLoc=currentTlb;
		(void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
		
	}
	else if(which== AddressErrorException)
	{
		cout<<"~~ AddressErrorException\n";
		interrupt->Halt();
	}
	else {
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
      interrupt->Halt();
    }
}


